package com.db.chat.client;

import com.db.chat.client.network.Configuration;
import com.db.chat.client.network.Network;
import com.db.chat.client.network.NetworkException;
import com.db.chat.logging.AbstractLogger;
import com.db.chat.logging.LoggerException;

import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.Collections;
import java.util.LinkedList;

/**
 * Created by Student on 25.11.2014.
 */
public class ClientStub implements Runnable , Closeable {

    private LinkedList<String> bufferedMessages;
    private Socket socket;
    private Network network;
    private AbstractLogger logger;
    private LinkedList<String> offlineCache;
    private boolean connected = false;

    private final Object bufferMonitor = new Object();

    public ClientStub(AbstractLogger logger) {
        this.logger = logger;
        this.offlineCache = new LinkedList<String>();

        try {
            Connect();
        } catch (IOException e) {
            logger.write("Cannot connect to server");
            System.out.println("Cannot connect to server. Client is waiting ...");
            //exit(-10);
        } catch (LoggerException e) {
            e.printStackTrace();
        }

        bufferedMessages = new LinkedList<>();
    }

    private void Connect() throws IOException, LoggerException {
        socket = new Socket(Configuration.getIpAddress(), Configuration.getPort());
        network = new Network(socket.getInputStream(),socket.getOutputStream());
        connected = true;
        System.out.println("Successful connection to server");
    }

    private void clearBuffer() {
        synchronized (bufferMonitor) {
            bufferedMessages.clear();
        }
    }

    public boolean gotMessages() {
        return !bufferedMessages.isEmpty();
    }

    public LinkedList<String> getBufferedMessages() {

        synchronized (bufferMonitor) {
            //LinkedList<String> tempBuffer = new LinkedList<>(Collections.nCopies(bufferedMessages.size(),null));
            LinkedList<String> tempBuffer = new LinkedList<>();
            tempBuffer.addAll(bufferedMessages);

            if (!bufferedMessages.isEmpty()) {
                Collections.copy(tempBuffer, bufferedMessages);
            }

            clearBuffer();
            return tempBuffer;
        }
    }

    public void sendMessage(String message) {

        if( connected ) {
            try {
                network.send(message);
            } catch (IOException e) {
                logger.write("Cannot send message to server");
            }
        }
        else {
            offlineCache.add(message);
        }
    }

    public void readServerMessage() throws NetworkException {
        String newMessage = network.receive();
        synchronized (bufferMonitor) {
            if (newMessage != null) {
                bufferedMessages.addLast(newMessage);
            }
        }
    }

    @Override
    public void run() {
        while(true) {
            if (Thread.currentThread().isInterrupted()) {
                return;
            }

            while (!connected) {
                try {
                    Thread.currentThread().sleep(Configuration.getReconnectTimeout());
                } catch (InterruptedException e) {
                    return;
                }

                try {
                    Connect();
                } catch (IOException e) {
                    logger.write("Cannot connect to server");
                    System.out.println("Cannot connect to server. Reconnection in " + Configuration.getReconnectTimeout() / 1000 + " seconds");
                } catch (LoggerException e) {
                    e.printStackTrace();
                }
            }

            try {
                if(!offlineCache.isEmpty()) {
                    for( String message : offlineCache )
                        sendMessage(message);

                    offlineCache.clear();
                }

                readServerMessage();
            } catch (NetworkException e) {
                logger.write("Error receiving message. Exiting");
                System.out.println("Error receiving message. Exiting");
                try {
                    close();
                } catch (IOException e1) {
                    logger.write("Error closing network connection");
                    System.out.println("Error closing network connection");
                }
            }
        }
    }

    @Override
    public void close() throws IOException {
        if( network != null )
            network.close();
        if( socket != null )
            socket.close();
        connected = false;
    }
}